home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / apps / ibrowse / ibrowse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  59.6 KB  |  2,164 lines

  1. /*
  2.  * Copyright 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.     ibrowse - Browse through a directory full of images.
  19.  
  20.     Tim Heidmann
  21.     Version 1.2.2
  22.     January 18, 1994
  23.  
  24.     copyright 1993, Silicon Graphics
  25. */
  26.  
  27. #include <gl.h>
  28. #include <fmclient.h>
  29. #include <device.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #include <sys/errno.h>
  33. #include <stdlib.h>
  34. #include <limits.h>
  35. #include <dirent.h>
  36. #include <math.h>
  37. #include <string.h>
  38. #include <stdio.h>
  39. #include <fcntl.h>
  40. #include <time.h>
  41. #include <sys/time.h>
  42. #include <sys/prctl.h>
  43. #include <signal.h>    /* SIGKILL */
  44. #include "ibrowse.h"
  45.  
  46. /* Colors */
  47. #define ICON_BG       0xff404040
  48. #define ICON_FRAME    0xff0000a0
  49. #define ICON_TEXT     0xffffffff
  50. #define ICON_DROP     0xff181818
  51. #define ICON_ACTIVE   0xff00ffff
  52. #define INFO_BG       0xff808080
  53. #define INFO_NAME     0xff000000
  54. #define INFO_TEXT     0xff404040
  55. #define INFO_FRAME    0xff000000
  56. #define SCROLL_BG     0xff202020
  57. #define SCROLL_FRAME  0xff000000
  58. #define SCROLL_THUMB  0xffa0a0a0
  59.  
  60. #define FDTIME(a,b) \
  61.     ((a).tv_sec - (b).tv_sec + 0.000001 * ((a).tv_usec - (b).tv_usec))
  62.  
  63. /*
  64.  * Image driver routines.  Add to this list to recognize new image types.
  65.  */
  66. int   Check_SGI(struct iconDirStruct *ids, int fd);
  67. int Iconify_SGI(struct iconDirStruct *ids, unsigned long *ip);
  68. char  *Info_SGI(struct iconDirStruct *ids, char *buf);
  69. void   Open_SGI(struct iconDirStruct *ids);
  70.  
  71. #ifdef IL
  72. int   Check_IL(struct iconDirStruct *ids, int fd);
  73. int Iconify_IL(struct iconDirStruct *ids, unsigned long *ip);
  74. char  *Info_IL(struct iconDirStruct *ids, char *buf);
  75. void   Open_IL(struct iconDirStruct *ids);
  76. #endif
  77.  
  78. struct ImageFnStruct ImageFn[] = {
  79.     1001, Check_SGI, Iconify_SGI, Info_SGI, Open_SGI
  80.  
  81. #ifdef IL
  82.   , 1002, Check_IL,  Iconify_IL,  Info_IL,  Open_IL
  83. #endif
  84. };
  85. int nImageTypes = sizeof(ImageFn) / sizeof(struct ImageFnStruct);
  86.  
  87.  
  88. /*****************************************************************************
  89.  *
  90.  * Just a few global variables...
  91.  *
  92.  *****************************************************************************/
  93.  
  94. /* Arguments and flags */
  95. char *cmdName;                 /* Name by which this program was invoked.    */
  96. char *argDirName;              /* Pointer to opt command line directory arg. */
  97. int neverWriteIconFiles;       /* -n flag: inhibits writing icon files.      */
  98. int updateOnlyFlag;            /* -u flag: update icon files offline. No UI. */
  99. int useIconFiles;              /* -f <name>: browse icon file. Don't scan dir*/
  100. char *iconRootName;            /* Pointer to icon file root name argument.   */
  101. int switchModes;               /* Switch between single/dbl buffer mode? */
  102.  
  103. /* Environment variables passed to shell Open commands */
  104. char envName[MAXNAMLEN+32] = "";
  105. char envCurDir[PATH_MAX+32] = "";
  106. char envOrigDir[PATH_MAX+32] = "";
  107. char envModifier[32] = "";
  108. char envRootName[MAXNAMLEN+32] = "";
  109.  
  110. /* Directory file info */
  111. #define MAXDIRENTS 2048
  112. int nDirEnts;
  113. struct iconDirStruct *myDirList[MAXDIRENTS];
  114.                    /* In-memory list of all files in directory.  */
  115.  
  116. int iconDirFD, iconFD;
  117. char  theDirName[MAXNAMLEN+1]; /* Full pathname of current browse directory. */
  118. char  newDirName[MAXNAMLEN+1]; /* Relative name of directory to switch to.   */
  119. char origDirName[MAXNAMLEN+1]; /* Pathname of working directory on startup.  */
  120. int canWriteIconFiles;         /* Are icon files writable.                   */
  121. int nIconFileEnts;             /* Number fo slots in icon file.              */
  122. int iconRef[MAXDIRENTS];       /* Is icon file slot in use.                  */
  123.  
  124. /* Display list info */
  125. int displayList[MAXICONS];     /* Order icons appear on the screen.          */
  126. int nIcons = 0;                /* How many icons have been found.            */
  127. int nDisplayIcons = 0;         /* How many icons are in display list.        */
  128. unsigned long *(icons[MAXICONS]);
  129.                    /* Pointers to icon bitmaps in memory.        */
  130. char     *fontNameList[] = {"Rock", "Screen"};
  131. int   iconFontSizeList[] = {    12,       12};
  132. int   infoFontSizeList[] = {    18,       18};
  133. int nFonts = sizeof(fontNameList) / sizeof(char *);
  134. fmfonthandle iconFont, infoFont;
  135.  
  136. /* Window position, size, view info */
  137. long win_sx, win_sy, win_ox, win_oy;
  138. int icon_nx = -1, icon_ny, icon_sx, icon_sy;
  139. int icon_xmargin = 40, icon_ymargin = 30;
  140. int scroll_bar_width = 20, scroll_bar_height;
  141. int scroll_thumb_min_height = 20, scroll_thumb_top, scroll_thumb_bottom;
  142. int info_left_margin = 20, info_line_space = 20;
  143. int info_area_height = 130;
  144. int icon_field_height, icon_field_top;
  145. int iFirstVisible = 0, iLastVisible;
  146.  
  147. /* Interaction state */
  148. #define DBL_CLICK 0.5
  149. #define EVENTUALLY 1.0
  150. #define MAXMENULINES 30
  151. #define MAXROLLOVERS  5
  152. int isScrolling = FALSE;
  153. int scroll_start_y, scroll_start_ift;
  154. float scroll_factor;
  155. int activeIcon = -1;               /* Which icon is highlighted?             */
  156. int newActiveIcon;                 /* Icon cursor is over - needs hilite.    */
  157. struct timeval lastClick = {0,0}, lastDraw = {0,0};
  158. int windowActive;                  /* Is the cursor in the window?           */
  159. int smoothScrolling = TRUE;
  160. long mainMenu, directoryMenu[MAXROLLOVERS];
  161. int directoryList[MAXDIRENTS];     /* Index to all directories in myDirList. */
  162. int nDirList = 0;                  /* # of sub-directories in menu.          */
  163. int nBusyDirList = 0;              /* # of sub-directories currently counted.*/
  164. #define MAXANCESTORS 512
  165. char *ancestorList[MAXANCESTORS];  /* Ptrs to each dir name in theDirName.   */
  166. int ancestorLen[MAXANCESTORS];     /* Length of each dir name.               */
  167. int nAncestors = 0;                /* # of ancestor-directories.             */
  168. int modKeys = 0;                   /* Current state of all the modifier keys */
  169. #define SETBIT(v, b, f) {(v)&=~((int)b); if (f) (v)|=((int)b);}
  170.  
  171. /* Idle processing */
  172. int readerPID;
  173. enum {Before, ReadDir, Register, CheckTimes, Checking, ReadIcons, Iconifying,
  174.     Compacting, SaveFiles, Done}
  175.     idleMode = Before;
  176. int idleFile = 0;
  177. DIR *theDir; /* Used for CheckTimes */
  178.  
  179.  
  180. /* Program operation flags - set by reader and display processes,
  181.  * checked and cleared by display process.
  182.  */
  183. int needToResize,                   /* Window size may have changed.         */
  184.     needToDraw,                     /* Need to redraw on next cycle.         */
  185.     needToDrawEventually,           /* Redraw before too long.               */
  186.     needToDrawActive,               /* Current highlighted icon has changed. */
  187. /* Set by display process, checked and cleared by reader process. */
  188.     needToSwitch = FALSE,           /* New directory name in newDirName.     */
  189.     needToFinish = FALSE,           /* Close down shop for exiting.          */
  190.     needToReadQuick = FALSE,        /* Visible icon needs to be read.        */
  191.     needToIconifyQuick = FALSE,     /* Visible icon needs to be created.     */
  192. /* Set by reader process, checked and cleared by display process. */
  193.     switchDone,                     /* State switched, okay to proceed.      */
  194.     okayToFinish,                   /* All finished, okay to exit.           */
  195.     needToUpdateDirectory = FALSE,  /* Need to remake Change Directory menu. */
  196.     needToCount,                    /* nIcons may have changed. Reformat.    */
  197. /* Set, checked, and cleared by reader process. */
  198.     needToWriteDirFile;             /* dir list entry has been marked dirty. */
  199.  
  200.  
  201.  
  202. /* Forward definitions - display/interaction process */
  203. void ParseArgs(int c, char *v[]);
  204. void Usage();
  205. void InitGfx();
  206. void SizeWindow();
  207. void SizeScrollBar();
  208. void LimitScrollBar();
  209. void MakeMenu();
  210. void MakeDirMenu(int depth, int iStart, int nEnts);
  211. void ChangeDirectory(char *name);
  212. void GetAncestors(char *thePath);
  213. void DoLeftMouse(int val);
  214. int  WhichIcon(int x, int y);
  215. void SetEnvVars(struct iconDirStruct *ids);
  216. int  DoMenu();
  217. int  SortDisplayList(int (*fn)(int *, int *));
  218. int  CmpDirName(int *d1, int *d2);
  219. int  CmpDirDate(int *d1, int *d2);
  220. int  CmpDirSize(int *d1, int *d2);
  221. void DoInputChange(int val);
  222. void DoTheWork();
  223. void Redraw();
  224. void WhereIsIcon(int iDList, int *icon_i, int *icon_j, int *xsize, int *ysize,
  225.     int *xoff, int *yoff, int *xorg, int *yorg);
  226. void DrawIcon(int iDList);
  227. void DrawIconInfo();
  228. void Commafy(char *s, int n);
  229. void DrawGenericInfo();
  230. void DrawScrollBar();
  231. void RedrawActive();
  232.  
  233. /* Forward definitions - Reader process */
  234. void IdleLoop();
  235. void IdleProcess();
  236. void StartDirectory(char *name);
  237. void FinishDirectory();
  238. void WriteDirFile();
  239. void WriteDirEnt(int iEnt);
  240. void FileIdleLoop();
  241. void FileIdleProcess();
  242. void OpenIconFiles();
  243. void StartReadDir();
  244. int  ProcessReadDir();
  245. void StartRegister();
  246. int  ProcessRegister();
  247. void StartCheckTimes();
  248. int  ProcessCheckTimes();
  249. void StartChecking();
  250. int  ProcessChecking();
  251. void StartReadIcons();
  252. int  ProcessReadIcons();
  253. void StartIconifying();
  254. int  ProcessIconifying();
  255. void StartCompacting();
  256. int  ProcessCompacting();
  257. void StartSaveFiles();
  258. int  ProcessSaveFiles();
  259. void StartDone();
  260. void CheckFile(char *filename);
  261. int  CheckImage(struct iconDirStruct *ids);
  262. void ImageToIcon(struct iconDirStruct *ids);
  263. void AddIcon(int iEnt);
  264. void AssignIcon(struct iconDirStruct *ids);
  265. void AllocateIcon(struct iconDirStruct *ids);
  266. void ReadIcon(struct iconDirStruct *ids);
  267. void WriteIcon(struct iconDirStruct *ids);
  268. char * GetStateName(int state);
  269. char * GetIdleModeName(int mode);
  270.  
  271.  
  272. static double PtoPfactor = -1;
  273.  
  274. float pixtopoints(float npix)
  275. {
  276.  
  277.     if(PtoPfactor < 0.0)
  278.         PtoPfactor = (72.0*getgdesc(GD_XMMAX)/25.4)/getgdesc(GD_XPMAX);
  279.     return npix*PtoPfactor;
  280. }
  281.  
  282. float pointstopix(float npoints)
  283. {
  284.     if(PtoPfactor < 0.0)
  285.         PtoPfactor = (72.0*getgdesc(GD_XMMAX)/25.4)/getgdesc(GD_XPMAX);
  286.     return npoints/PtoPfactor;
  287. }
  288.  
  289. /*****************************************************************************
  290.  *
  291.  * Controlling display and interaction process routines.  Spawn the reader
  292.  * process and communicate with it via flags, loop looking for events, handle
  293.  * user interaction, redraw the display when necessary.
  294.  *
  295.  *****************************************************************************/
  296.  
  297. main(int argc, char *argv[]) {
  298.     short dev, val;
  299.     int done;
  300.  
  301.     ParseArgs(argc, argv);
  302.     InitGfx();
  303.  
  304.     prctl(PR_SETEXITSIG, SIGKILL); /* If one process dies, kill all. */
  305.     if (useIconFiles)
  306.     sproc(FileIdleLoop, PR_SALL);
  307.     else {
  308.     sproc(IdleLoop, PR_SALL);
  309.     ChangeDirectory(argDirName);
  310.     }
  311.  
  312.     if (updateOnlyFlag) {
  313.     /* Skip the user interface and display. Wait for update to finish. */
  314.     while (idleMode != Done) sginap(5);
  315.  
  316.     } else {
  317.     /* Run fully interactive. */
  318.     for (done = FALSE; !done; ) {
  319.         /* Read all the pending events.  */
  320.         while (qtest()) {
  321.         switch (dev = qread(&val)) {
  322.         case ESCKEY:
  323.         case QKEY:
  324.         case WINQUIT:
  325.         case WINCLOSE:
  326.         case WINSHUT:
  327.             done = TRUE; break;
  328.  
  329.         case LEFTMOUSE:   DoLeftMouse(val);                  break;
  330.         case RIGHTMOUSE:  if (val && DoMenu()) done = TRUE;  break;
  331.         case REDRAW:      needToResize = TRUE;               break;
  332.         case INPUTCHANGE: DoInputChange(val);                break;
  333.  
  334.         case LEFTSHIFTKEY:
  335.         case RIGHTSHIFTKEY: SETBIT(modKeys, SHIFTKEYBIT, val); break;
  336.         case LEFTCTRLKEY:
  337.         case RIGHTCTRLKEY:  SETBIT(modKeys,  CTRLKEYBIT, val); break;
  338.         case LEFTALTKEY:
  339.         case RIGHTALTKEY:   SETBIT(modKeys,   ALTKEYBIT, val); break;
  340.  
  341.         default: break;
  342.         }
  343.         }
  344.         
  345.         /* Handle the events. */
  346.         if (done) break;
  347.         DoTheWork();
  348.     }
  349.     }
  350.  
  351.     /* Wait for reader process to clean up files and finish. */
  352.     needToFinish = TRUE;
  353.     while (!okayToFinish) sginap(1);
  354. }
  355.  
  356.  
  357. void
  358. ParseArgs(int c, char *v[]) {
  359.     cmdName = v[0];
  360.     argDirName = NULL;
  361.     neverWriteIconFiles = FALSE;
  362.     updateOnlyFlag = FALSE;
  363.     useIconFiles = FALSE;
  364.     getwd(origDirName);
  365.  
  366.     for (c--, v++; c > 0; c--, v++) {
  367.     if (v[0][0] == '-') {
  368.         /* Parse a command line option */
  369.         switch (v[0][1]) {
  370.         case 'n': /* Don't update .ibrowse files in this directory */
  371.         neverWriteIconFiles = TRUE;
  372.         break;
  373.         case 's': /* Switch to singlebuffer mode when not scrolling. */
  374.         switchModes = TRUE;
  375.         break;
  376.         case 'u': /* Update .ibrowse files. Don't run user interface. */
  377.         updateOnlyFlag = TRUE;
  378.         break;
  379.         case 'f': /* Specify root name of .dir and .icon files to scan. */
  380.         useIconFiles = TRUE;
  381.         iconRootName = v[1];
  382.         strcpy(theDirName, origDirName);
  383.         c--, v++;
  384.         break;
  385.         default:
  386.         Usage();
  387.         }
  388.     }
  389.     else {
  390.         /* Parse a directory name */
  391.         if (argDirName != NULL) Usage();
  392.         argDirName = v[0];
  393.     }
  394.     }
  395.  
  396.     /* Default to the current directory. */
  397.     if (argDirName == NULL) argDirName = ".";
  398. }
  399.  
  400. void
  401. Usage() {
  402.     fprintf(stderr, "usage: %s [options] [<directory>]\n", cmdName);
  403.     fprintf(stderr, "options:\n");
  404.     fprintf(stderr, "       -n    Don't write icon files.\n");
  405.     fprintf(stderr, "       -s    Switch to single-buffer mode when not "
  406.             "scrolling.\n");
  407.     fprintf(stderr, "       -u    Update icon files only. No display.\n");
  408.     fprintf(stderr, "       -f <name>\n");
  409.     fprintf(stderr, "             Browse <name>.dir and <name>.icon files. ");
  410.     fprintf(stderr, "Don't scan directories.\n");
  411.     exit(1);
  412. }
  413.  
  414. void
  415. InitGfx() {
  416.     int iFont, i;
  417.     char title_buf[PATH_MAX + 64];
  418.     int iconFontSize, infoFontSize;
  419.     char fontName[128], *fontSpec;
  420.     fmfonthandle theFont, theIconFont, theInfoFont;
  421.  
  422.     /* No graphics for update only */
  423.     if (updateOnlyFlag) return;
  424.  
  425.     minsize(scroll_bar_width + 4*(ICONSIZE+icon_xmargin) + icon_xmargin,
  426.         info_area_height + 1*(ICONSIZE+icon_ymargin) + icon_ymargin);
  427.     stepunit(ICONSIZE + icon_xmargin, ICONSIZE + icon_ymargin);
  428.     fudge(
  429.     scroll_bar_width + ICONSIZE + 2 * icon_xmargin,
  430.     info_area_height + ICONSIZE + 2 * icon_ymargin);
  431.     if (useIconFiles)
  432.     sprintf(title_buf, "%s %s", cmdName, iconRootName);
  433.     else
  434.     sprintf(title_buf, "%s %s", cmdName, theDirName);
  435.     winopen(title_buf);
  436.  
  437.     minsize(0, 0);
  438.     stepunit(ICONSIZE + icon_xmargin, ICONSIZE + icon_ymargin);
  439.     fudge(
  440.     scroll_bar_width + ICONSIZE + 2 * icon_xmargin,
  441.     info_area_height + ICONSIZE + 2 * icon_ymargin);
  442.     winconstraints();
  443.  
  444.     if (!switchModes) doublebuffer();
  445.     RGBmode();
  446.     gconfig();
  447.     cpack(ICON_BG);
  448.     clear();
  449.     swapbuffers();
  450.  
  451.     getorigin(&win_ox, &win_oy);
  452.     getsize(&win_sx, &win_sy);
  453.     reshapeviewport();
  454.     ortho2(0, win_sx-1, 0, win_sy-1);
  455.     SizeWindow();
  456.  
  457.     qdevice(ESCKEY);
  458.     qdevice(QKEY);
  459.     qdevice(LEFTMOUSE);
  460.     qdevice(RIGHTMOUSE);
  461.     qdevice(LEFTSHIFTKEY); qdevice(RIGHTSHIFTKEY);
  462.     qdevice(LEFTALTKEY);   qdevice(RIGHTALTKEY);
  463.     qdevice(LEFTCTRLKEY);  qdevice(RIGHTCTRLKEY);
  464.     qdevice(WINCLOSE);
  465.     qdevice(WINQUIT);
  466.     qdevice(WINSHUT);
  467.     qenter(REDRAW, 0);
  468.  
  469.     fminit();
  470.     /* Look for environment variable specs for the fonts to use */
  471.     if ((fontSpec = getenv("IBROWSE_ICON_FONT")) != NULL) {
  472.     iconFontSize = 12;
  473.     sscanf(fontSpec, "%s %d", fontName, &iconFontSize);
  474.     theFont = fmfindfont(fontName);
  475.     iconFont = fmscalefont(theFont, pixtopoints(iconFontSize));
  476.     }
  477.     if ((fontSpec = getenv("IBROWSE_INFO_FONT")) != NULL) {
  478.     infoFontSize = 18;
  479.     sscanf(fontSpec, "%s %d", fontName, &infoFontSize);
  480.     theFont = fmfindfont(fontName);
  481.     infoFont = fmscalefont(theFont, pixtopoints(infoFontSize));
  482.     }
  483.     
  484.     /* Supply default fonts if necessary */
  485.     if (iconFont == 0 || infoFont == 0) {
  486.     for (iFont = 0, theFont = 0; iFont < nFonts; iFont++) 
  487.         if ((theFont = fmfindfont(fontNameList[iFont])) != 0) {
  488.         if (iconFont == 0) {
  489.             iconFontSize = iconFontSizeList[iFont];
  490.             iconFont = fmscalefont(theFont, pixtopoints(iconFontSize));
  491.         }
  492.         if (infoFont == 0) {
  493.             infoFontSize = infoFontSizeList[iFont];
  494.             infoFont = fmscalefont(theFont, pixtopoints(infoFontSize));
  495.         }
  496.         break;
  497.         }
  498.  
  499.     if (theFont == 0) {
  500.         fprintf(stderr, "Can't find any desirable fonts!\n");
  501.         exit(1);
  502.     }
  503.     }
  504.  
  505.     mainMenu = newpup();
  506.     for (i=0; i<MAXROLLOVERS; i++) directoryMenu[i] = newpup();
  507.     MakeMenu();
  508. }
  509.  
  510. void
  511. SizeWindow() {
  512.     int old_icon_field_height;
  513.  
  514.     /* Set variables and graphics to reflect window of size win_sx by win_sy,
  515.        with nIcons icons. */
  516.     needToCount = FALSE;
  517.     nDisplayIcons = nIcons;
  518.     icon_sx = win_sx - scroll_bar_width;
  519.     scroll_bar_height =
  520.     icon_sy = win_sy - info_area_height;
  521.     icon_nx = (icon_sx - icon_xmargin) / (ICONSIZE + icon_xmargin);
  522.     if (icon_nx < 1) icon_nx = 1;
  523.     icon_ny = (nDisplayIcons + icon_nx - 1) / icon_nx;
  524.     old_icon_field_height = icon_field_height;
  525.     icon_field_height = icon_ymargin +
  526.     icon_ny * (ICONSIZE + icon_ymargin);
  527.     if (icon_field_height != old_icon_field_height) {
  528.     /* Layout has changed, pick a new distance from top to display. */
  529.     icon_field_top = icon_field_height -
  530.         (iFirstVisible / icon_nx) * (ICONSIZE + icon_ymargin);
  531.     LimitScrollBar();
  532.     }
  533.     SizeScrollBar();
  534. }
  535.  
  536. void
  537. SizeScrollBar() {
  538.     int sb_hgt, ify, iRow;
  539.  
  540.     /* Calculate position and range of scroll bar thumb */
  541.     scroll_thumb_top = info_area_height +
  542.     (scroll_bar_height - scroll_thumb_min_height) *
  543.     ((float) icon_field_top) / icon_field_height +
  544.     scroll_thumb_min_height;
  545.     sb_hgt = (scroll_bar_height - scroll_thumb_min_height) *
  546.     ((float) icon_sy) / icon_field_height;
  547.     scroll_thumb_bottom = scroll_thumb_top - sb_hgt - scroll_thumb_min_height;
  548.     if (scroll_thumb_bottom < info_area_height)
  549.     scroll_thumb_bottom = info_area_height;
  550.     
  551.     /* Calculate range of visible icons.
  552.        ify is pixel distance into icon field from top of window. */
  553.     ify = icon_field_height - icon_field_top;
  554.     iRow = (ify - icon_ymargin) / (ICONSIZE + icon_ymargin);
  555.     iFirstVisible = iRow * icon_nx;
  556.     if (iFirstVisible < 0) iFirstVisible = 0;
  557.     iRow = (ify - icon_ymargin + icon_sy) / (ICONSIZE + icon_ymargin);
  558.     iLastVisible = (iRow + 1) * icon_nx - 1;
  559.     if (iLastVisible >= nDisplayIcons) iLastVisible = nDisplayIcons - 1;
  560. }
  561.  
  562. void
  563. LimitScrollBar() {
  564.     /* Try for no empty space in the window below the icons... */
  565.     if (icon_field_top < icon_sy)
  566.     icon_field_top = icon_sy;
  567.     /* ...but definitely no empty space in the window above the icons */
  568.     if (icon_field_top > icon_field_height)
  569.     icon_field_top = icon_field_height;
  570. }
  571.  
  572.  
  573. void
  574. MakeMenu() {
  575.     freepup(mainMenu);
  576.     mainMenu = defpup("Image Browser%t");
  577.  
  578.     /* Make the "Change Directory" Menu */
  579.     if (!useIconFiles && (nAncestors > 0 || nDirList > 0)) {
  580.     MakeDirMenu(0, 0, nAncestors + nDirList);
  581.     addtopup(mainMenu, "Change Directory%m%l", directoryMenu[0]);
  582.     }
  583.     
  584.     /* Allow picking some options */
  585.     addtopup(mainMenu, "Sort by Name%x10");
  586.     addtopup(mainMenu, "Sort by Size%x11");
  587.     addtopup(mainMenu, "Sort by Date%x12%l");
  588.     addtopup(mainMenu, !smoothScrolling ? "Smooth Scrolling On %x20%l" :
  589.                                           "Smooth Scrolling Off%x21%l");
  590.     addtopup(mainMenu, "About...%x90");
  591.     addtopup(mainMenu, "Exit%x99");
  592. }
  593.  
  594. void
  595. MakeDirMenu(int depth, int iStart, int nEnts) {
  596.     int iEnt, nLocalEnts, i, iItem, isGray;
  597.     char buf[MAXNAMLEN], buf2[MAXNAMLEN];
  598.  
  599.     freepup(directoryMenu[depth]);
  600.     directoryMenu[depth] = newpup();
  601.     nLocalEnts = nEnts > MAXMENULINES ? MAXMENULINES - 1 : nEnts;
  602.     iItem = 1;
  603.  
  604.     /* Need rollover submenus to fit all the names? */
  605.     if (nLocalEnts != nEnts && depth < MAXROLLOVERS) {
  606.     MakeDirMenu(depth + 1, iStart + nLocalEnts, nEnts - nLocalEnts);
  607.     addtopup(directoryMenu[depth], "more...%l%m", directoryMenu[depth + 1]);
  608.     iItem++;
  609.     }
  610.  
  611.     /* Add the names for this rollover */
  612.     for (i = iStart; i < iStart + nLocalEnts; i++) {
  613.     isGray = FALSE;
  614.     if (i < nAncestors) {
  615.         /* Make a menu item for an ancestor of the current directory */
  616.         strncpy(buf2, ancestorList[i], ancestorLen[i]);
  617.         buf2[ancestorLen[i]] = '\0';
  618.         if (i == nAncestors - 1) {
  619.         /* Last ancestor - the current directory.  Gray it out. */
  620.         sprintf(buf, "%s%%x%d%%l", buf2, 10000+i);
  621.         isGray = TRUE;
  622.         } else
  623.         sprintf(buf, "%s%%x%d", buf2, 10000+i);
  624.     } else
  625.         /* Make a menu item for subdirectories of the current directory */
  626.         sprintf(buf, "%s%%x%d",
  627.         myDirList[directoryList[i - nAncestors]]->name,
  628.         100 + i - nAncestors);
  629.  
  630.     addtopup(directoryMenu[depth], buf);
  631.     if (isGray) setpup(directoryMenu[depth], iItem, PUP_GREY);
  632.     iItem++;
  633.     }
  634. }
  635.  
  636. void
  637. ChangeDirectory(char *name) {
  638.     char buf[PATH_MAX];
  639.  
  640.     /* Clear out the Change Directory menu before switching */
  641.     if (!updateOnlyFlag) {
  642.     nAncestors = 0;
  643.     nDirList = 0;
  644.     MakeMenu();
  645.     }
  646.  
  647.     /* Direct and wait for reader to close current, switch to new */
  648.     strcpy(newDirName, name);
  649.     switchDone = FALSE;
  650.     needToSwitch = TRUE;
  651.     while (!switchDone) sginap(5);
  652.  
  653.     /* Put current directory path in window title. Update directory menu. */
  654.     if (!updateOnlyFlag) {
  655.     sprintf(buf, "%s %s", cmdName, theDirName);
  656.     wintitle(buf);
  657.     GetAncestors(theDirName);
  658.     MakeMenu();
  659.     }
  660. }
  661.  
  662. void
  663. GetAncestors(char *thePath) {
  664.     /* Save pointers to each parent directory in the given pathname, plus its
  665.      * length. Ptrs in ancestorList, len in ancestorLen, # in nAncestors.
  666.      * "/" counts as one.  Multiple /'s are ignored.
  667.      * String is assumed to start with '/'.
  668.      */
  669.     ancestorList[0] = thePath;
  670.     ancestorLen[0] = 1;
  671.     nAncestors = 1;
  672.     for (;;) {
  673.     for (; *thePath == '/'; thePath++) ;
  674.     if (*thePath == '\0') return;
  675.  
  676.     /* Mark the start of a new directory name and scan it all */
  677.     ancestorList[nAncestors] = thePath;
  678.     for (; *thePath != '\0' && *thePath != '/'; thePath++) ;
  679.  
  680.     /* End of name or end of path.  Calc length and return or continue */
  681.     ancestorLen[nAncestors] = thePath - ancestorList[nAncestors];
  682.     nAncestors++;
  683.     if (*thePath == '\0') return;
  684.     }
  685. }
  686.  
  687. void
  688. DoLeftMouse(int val) {
  689.     int openIcon, mx, my;
  690.     struct timeval thisTime;
  691.     struct iconDirStruct *ids;
  692.     char buf[PATH_MAX + 64];
  693.     char *openCmd;
  694.  
  695.     if (val) {
  696.     /* This is a left mouse button press.*/
  697.     mx = getvaluator(MOUSEX) - win_ox;
  698.     my = getvaluator(MOUSEY) - win_oy;
  699.  
  700.     if (my < info_area_height) {
  701.         /* In the info area - do nothing */
  702.  
  703.     } else if (mx < scroll_bar_width) {
  704.         /* In the scroll bar */
  705.         if (icon_field_height < icon_sy) {
  706.         /* Nowhere to scroll - do nothing */
  707.         } else if (my < scroll_thumb_bottom) {
  708.         /* In the space below the thumb */
  709.         icon_field_top -= smoothScrolling ? icon_sy :
  710.             (icon_sy/(ICONSIZE+icon_ymargin))*(ICONSIZE+icon_ymargin);
  711.         LimitScrollBar();
  712.         SizeScrollBar();
  713.         needToDraw = TRUE;
  714.         } else if (my > scroll_thumb_top) {
  715.         /* In the space above the thumb */
  716.         icon_field_top += smoothScrolling ? icon_sy :
  717.             (icon_sy/(ICONSIZE+icon_ymargin))*(ICONSIZE+icon_ymargin);
  718.         LimitScrollBar();
  719.         SizeScrollBar();
  720.         needToDraw = TRUE;
  721.         } else {
  722.         /* In the thumb itself - start scrolling */
  723.         isScrolling = TRUE;
  724.         scroll_start_y = my;
  725.         scroll_start_ift = icon_field_top;
  726.         scroll_factor = icon_field_height /
  727.             (float) scroll_bar_height;
  728.  
  729.         /* Scroll in double buffer mode to avoid flicker */
  730.         if (switchModes) {
  731.             cpack(0);
  732.             clear();
  733.             doublebuffer();
  734.             gconfig();
  735.         }
  736.         }
  737.  
  738.     } else {
  739.         /* In the icon field */
  740.         gettimeofday(&thisTime, NULL);
  741.         openIcon = WhichIcon(mx, my);
  742.         if (FDTIME(thisTime, lastClick) < DBL_CLICK
  743.             && openIcon >= 0) {
  744.         ids = myDirList[displayList[openIcon]];
  745.         percentdone(50.0);
  746.         SetEnvVars(ids);
  747.         if ((openCmd = getenv("IBROWSE_OPEN_CMD")) != NULL)
  748.             system(openCmd);
  749.         else
  750.             (*ImageFn[ids->iType].Open)(ids);
  751.         percentdone(100.0);
  752.         }
  753.         lastClick = thisTime;
  754.     }
  755.  
  756.     } else {
  757.     /* This is a left mouse button release.*/
  758.     if (isScrolling) {
  759.         isScrolling = FALSE;
  760.  
  761.         /* Done scrolling. Go back to sngl buffer for best display. */
  762.         if (switchModes) {
  763.         singlebuffer();
  764.         gconfig();
  765.         needToDraw = TRUE;
  766.         }
  767.     }
  768.     }
  769. }
  770.  
  771. int
  772. WhichIcon(int x, int y) {
  773.     int ifx, ify, iRow, iCol, iIcon;
  774.  
  775.     if (x < scroll_bar_width || y < info_area_height ||
  776.         x > win_sx || y > win_sy)
  777.     return -1;
  778.  
  779.     /* Which row and column? Is the cursor exactly on the icon? */
  780.     /* ify is pixels down from top of icon field, ifx from left */
  781.     ify = (win_sy - y) + (icon_field_height - icon_field_top);
  782.     if (ify % (ICONSIZE + icon_ymargin) < icon_ymargin) return -1;
  783.     iRow = ify / (ICONSIZE + icon_ymargin);
  784.     ifx = x - scroll_bar_width;
  785.     if (ifx % (ICONSIZE + icon_xmargin) < icon_xmargin) return -1;
  786.     iCol = ifx / (ICONSIZE + icon_xmargin);
  787.  
  788.     /* Return index to display list element */
  789.     iIcon = iRow * icon_nx + iCol;
  790.     return (iIcon >= nIcons) ? -1 : iIcon;
  791. }
  792.  
  793. void
  794. SetEnvVars(struct iconDirStruct *ids) {
  795.     /* Set the shell environment variables passed to Open commands.
  796.      * The environment variable strings are static variables since they
  797.      * have to hang around for a while.
  798.      */
  799.     sprintf(envName, "IBROWSE_NAME=%s", ids->name);
  800.     putenv(envName);
  801.     sprintf(envCurDir, "IBROWSE_CUR_DIR=%s", theDirName);
  802.     putenv(envCurDir);
  803.     sprintf(envOrigDir, "IBROWSE_ORIG_DIR=%s", origDirName);
  804.     putenv(envOrigDir);
  805.     sprintf(envModifier, "IBROWSE_MODIFIERS=%d", modKeys);
  806.     putenv(envModifier);
  807.     sprintf(envRootName, "IBROWSE_ROOT_NAME=%s", useIconFiles?iconRootName:"");
  808.     putenv(envRootName);
  809. }
  810.  
  811. int
  812. DoMenu() {
  813.     /* Return TRUE iff Exit is selected */
  814.     int iMenu, flag;
  815.     char buf[PATH_MAX];
  816.  
  817.     flag = FALSE;
  818.     switch (iMenu = dopup(mainMenu)) {
  819.     case 10: /* Sort Alphabetically */
  820.     SortDisplayList(CmpDirName); needToDraw = TRUE;
  821.     break;
  822.     case 11: /* Sort by Size */
  823.     SortDisplayList(CmpDirSize); needToDraw = TRUE;
  824.     break;
  825.     case 12: /* Sort by Age */
  826.     SortDisplayList(CmpDirDate); needToDraw = TRUE;
  827.     break;
  828.     
  829.     case 20: /* Smooth Scrolling On */
  830.     smoothScrolling = TRUE;
  831.     MakeMenu();
  832.     break;
  833.     case 21: /* Smooth Scrolling Off */
  834.     smoothScrolling = FALSE;
  835.     icon_field_top /= (ICONSIZE + icon_ymargin);
  836.     icon_field_top *= (ICONSIZE + icon_ymargin);
  837.     icon_field_top += icon_ymargin;
  838.     needToDraw = TRUE;
  839.     MakeMenu();
  840.     break;
  841.  
  842.     case 90: /* About... */
  843.     system("/usr/bin/X11/xconfirm "
  844.         "-h \"About...\" "
  845.         "-t \"ibrowse - version 1.2.2\" "
  846.         "-t \"    a quick hack written by Tim Heidmann\" "
  847.         "-t \"    copyright 1993, Silicon Graphics\" "
  848.         "> /dev/null");
  849.     break;
  850.  
  851.     case 99: flag = TRUE; break;
  852.  
  853.     default:
  854.     if (iMenu < 100) break;
  855.     else if (iMenu < 100 + nDirList)
  856.         ChangeDirectory(myDirList[directoryList[iMenu-100]]->name);
  857.     else if (iMenu < 10000) break;
  858.     else if (iMenu < 10000 + nAncestors) {
  859.         strncpy(buf, theDirName, ancestorList[iMenu-10000] - theDirName +
  860.         ancestorLen[iMenu-10000]);
  861.         buf[ancestorList[iMenu-10000] - theDirName +
  862.         ancestorLen[iMenu-10000]] = '\0';
  863.         ChangeDirectory(buf);
  864.     }
  865.     break;
  866.     }
  867.  
  868.     return flag;
  869. }
  870.  
  871. int
  872. SortDisplayList(int (*fn)(int *, int *)) {
  873.    qsort(displayList, nIcons, sizeof(int),
  874.     (int (*)(const void *, const void *)) fn);
  875. }
  876.  
  877. int
  878. CmpDirName(int *d1, int *d2) {
  879.     return (strcmp(myDirList[*d1]->name, myDirList[*d2]->name));
  880. }
  881.  
  882. int
  883. CmpDirSize(int *d1, int *d2) {
  884.     /* Put largest files first. Files of equal size sorted alphabetically */
  885.     return
  886.     myDirList[*d1]->size > myDirList[*d2]->size ? -1 :
  887.     myDirList[*d1]->size < myDirList[*d2]->size ?  1 :
  888.     strcmp(myDirList[*d1]->name, myDirList[*d2]->name);
  889. }
  890.  
  891. int
  892. CmpDirDate(int *d1, int *d2) {
  893.     /* Put newest files first. Files of the same age sorted alphabetically */
  894.     return
  895.     myDirList[*d1]->mtime > myDirList[*d2]->mtime ? -1 :
  896.     myDirList[*d1]->mtime < myDirList[*d2]->mtime ?  1 :
  897.     strcmp(myDirList[*d1]->name, myDirList[*d2]->name);
  898. }
  899.  
  900. void
  901. DoInputChange(int val) {
  902.     windowActive = val;
  903.     if (windowActive)
  904.     /* Mouse into window.  Check modifier keys. */
  905.     modKeys =
  906.         getbutton(LEFTSHIFTKEY)||getbutton(RIGHTSHIFTKEY)?SHIFTKEYBIT:0 |
  907.         getbutton(LEFTCTRLKEY) ||getbutton(RIGHTCTRLKEY) ? CTRLKEYBIT:0 |
  908.         getbutton(LEFTALTKEY)  ||getbutton(RIGHTALTKEY)  ?  ALTKEYBIT:0;
  909.     else
  910.     /* Mouse out of window. Deselect the active icon, redraw */
  911.     if (activeIcon >= 0) {
  912.         activeIcon = -1;
  913.         needToDraw = TRUE;
  914.     }
  915. }
  916.  
  917. void
  918. DoTheWork() {
  919.     int i, mx, my, delta;
  920.     long xnew, ynew;
  921.     struct timeval thisTime;
  922.  
  923.     if (needToUpdateDirectory) {
  924.     needToUpdateDirectory = FALSE;
  925.     nDirList = nBusyDirList;
  926.     MakeMenu();
  927.     }
  928.     if (needToResize) {
  929.     getorigin(&win_ox, &win_oy);
  930.     getsize(&xnew, &ynew);
  931.     if (xnew != win_sx || ynew != win_sy) {
  932.         win_sx = xnew;
  933.         win_sy = ynew;
  934.         reshapeviewport();
  935.         ortho2(0, win_sx-1, 0, win_sy-1);
  936.         SizeWindow();
  937.     }
  938.     needToDraw = TRUE;
  939.     needToResize = FALSE;
  940.     }
  941.     if (needToCount) SizeWindow();
  942.     if (isScrolling) {
  943.     my = getvaluator(MOUSEY) - win_oy;
  944.     delta = scroll_factor * (my - scroll_start_y);
  945.     if (!smoothScrolling) delta = delta /
  946.         (ICONSIZE + icon_ymargin) *
  947.         (ICONSIZE + icon_ymargin);
  948.     icon_field_top = scroll_start_ift + delta;
  949.  
  950.     LimitScrollBar();
  951.     SizeScrollBar();
  952.     needToDraw = TRUE;
  953.     } else if (windowActive) {
  954.     /* Hot mouse - show info about the icon currently touched */
  955.     mx = getvaluator(MOUSEX) - win_ox;
  956.     my = getvaluator(MOUSEY) - win_oy;
  957.     if ((newActiveIcon = WhichIcon(mx, my)) != activeIcon) {
  958.         needToDrawActive = TRUE;
  959.     }
  960.     }
  961.  
  962.     if (needToDrawEventually) {
  963.     gettimeofday(&thisTime, NULL);
  964.     if (FDTIME(thisTime, lastDraw) > EVENTUALLY) needToDraw = TRUE;
  965.     }
  966.     if (needToDraw) {
  967.     needToDraw = FALSE;
  968.     needToDrawEventually = FALSE;
  969.     if (needToDrawActive) activeIcon = newActiveIcon;
  970.     needToDrawActive = FALSE;
  971.     Redraw();
  972.     gettimeofday(&lastDraw, NULL);
  973.     /* If more icons have appeared, schedule another draw */
  974.     if (nDisplayIcons != nIcons) needToDrawEventually = TRUE;
  975.     } else if (needToDrawActive) {
  976.     needToDrawActive = FALSE;
  977.     RedrawActive();
  978.     } else
  979.     /* Not drawing; Slow down the busy loop */
  980.     sginap(1);
  981. }
  982.  
  983.  
  984. void
  985. Redraw() {
  986.     int iDList;
  987.  
  988.     /* Draw the icon field */
  989.     scrmask(scroll_bar_width, win_sx-1, info_area_height, win_sy-1);
  990.     cpack(ICON_BG);
  991.     rectf(scroll_bar_width, info_area_height, win_sx-1, win_sy-1);
  992.     fmsetfont(iconFont);
  993.     needToReadQuick = needToIconifyQuick = FALSE;
  994.     for (iDList = iFirstVisible; iDList <= iLastVisible; iDList++)
  995.     DrawIcon(iDList);
  996.  
  997.     /* Draw the info window */
  998.     scrmask(0, win_sx-1, 0, info_area_height-1);
  999.     cpack(INFO_BG);
  1000.     rectfi(0, 0, win_sx-1, info_area_height-1);
  1001.     cpack(INFO_FRAME);
  1002.     recti(0, 0, win_sx-1, info_area_height-1);
  1003.     fmsetfont(infoFont);
  1004.     if (activeIcon >= 0)
  1005.     DrawIconInfo();
  1006.     else
  1007.     DrawGenericInfo();
  1008.  
  1009.     /* Draw the scroll bar */
  1010.     scrmask(0, scroll_bar_width-1, info_area_height, win_sy-1);
  1011.     DrawScrollBar();
  1012.  
  1013.     swapbuffers();
  1014.     scrmask(0, win_sx-1, 0, win_sy-1);
  1015. }
  1016.  
  1017. void
  1018. DrawIcon(int iDList) {
  1019.     int icon_i, icon_j;
  1020.     int xorg, yorg, xoff, yoff, xsize, ysize;
  1021.     int name_width;
  1022.     struct iconDirStruct *ids;
  1023.  
  1024.     /* Calculate the position of this icon */
  1025.     WhereIsIcon(iDList,
  1026.     &icon_i, &icon_j, &xsize, &ysize, &xoff, &yoff, &xorg, &yorg);
  1027.  
  1028.     ids = myDirList[displayList[iDList]];
  1029.     if (ids->state == NEED2ICONIFY) {
  1030.     /* Image has not been iconified yet */
  1031.     needToIconifyQuick = TRUE;
  1032.     cpack(ICON_FRAME);
  1033.     recti(xorg+xoff, yorg+yoff, xorg+xoff+xsize, yorg+yoff+ysize);
  1034.     } else if (ids->state == ISIMAGE) {
  1035.     if (icons[ids->iicon] == NULL ||
  1036.         ((char *)(icons[ids->iicon]))[ICONSPACE] == 0) {
  1037.         /* icon pointer is NULL or extra byte at end of buffer has not
  1038.          * been set to non-zero:
  1039.          *     Icon hasn't been iconified or read from icon file yet.
  1040.          */
  1041.         needToReadQuick = TRUE;
  1042.         cpack(ICON_FRAME);
  1043.         recti(xorg+xoff, yorg+yoff, xorg+xoff+xsize, yorg+yoff+ysize);
  1044.     } else {
  1045.         /* Draw the icon's dropshadow and bitmap */
  1046.         cpack(ICON_DROP);
  1047.         rectfi(xorg+xoff+3, yorg+yoff-3,
  1048.         xorg+xoff+xsize+3, yorg+yoff+ysize-3);
  1049.         lrectwrite(xorg+xoff, yorg+yoff,
  1050.         xorg+xoff+xsize-1, yorg+yoff+ysize-1,
  1051.         icons[ids->iicon]);
  1052.     }
  1053.     }
  1054.     
  1055.     /* Label it */
  1056.     name_width = fmgetstrwidth(iconFont, ids->name);
  1057.     cpack(ICON_DROP);
  1058.     cmov2i(xorg + (ICONSIZE - name_width)/2 + 1, yorg - 15 - 1);
  1059.     fmprstr(ids->name);
  1060.     cpack(ICON_TEXT);
  1061.     cmov2i(xorg + (ICONSIZE - name_width)/2, yorg - 15);
  1062.     fmprstr(ids->name);
  1063.  
  1064.     /* Active? Frame it. */
  1065.     if (iDList == activeIcon) {
  1066.     cpack(ICON_ACTIVE);
  1067.     recti(xorg+xoff-4, yorg+yoff-4,
  1068.         xorg+xoff+xsize+4, yorg+yoff+ysize+4);
  1069.     }
  1070. }
  1071.  
  1072. void
  1073. WhereIsIcon(int iDList,
  1074.     int *icon_i, int *icon_j, int *xsize, int *ysize, int *xoff, int *yoff,
  1075.     int *xorg, int *yorg) {
  1076.     struct iconDirStruct *ids;
  1077.  
  1078.     /* Where does a particular icon live on the screen right now? */
  1079.     ids = myDirList[displayList[iDList]];
  1080.     *icon_j = iDList / icon_nx;
  1081.     *icon_i = iDList - *icon_j * icon_nx;
  1082.     *xsize = ids->ixsize;
  1083.     *ysize = ids->iysize;
  1084.     *xoff = (ICONSIZE - *xsize) / 2;
  1085.     *yoff = (ICONSIZE - *ysize) / 2;
  1086.     *xorg = scroll_bar_width + icon_xmargin +
  1087.     *icon_i * (ICONSIZE + icon_xmargin);
  1088.     *yorg = win_sy - (*icon_j + 1) * (ICONSIZE + icon_ymargin);
  1089.     *yorg += icon_field_height - icon_field_top;
  1090. }
  1091.  
  1092. void
  1093. DrawIconInfo() {
  1094.     struct iconDirStruct *ids;
  1095.     int info_y;
  1096.     char info_buf[PATH_MAX + 64], num_buf[64];
  1097.  
  1098.     ids = myDirList[displayList[activeIcon]];
  1099.  
  1100.     cpack(INFO_NAME);
  1101.     /* File name */
  1102.     info_y = info_area_height - info_line_space - 5;
  1103.     cmov2i(info_left_margin, info_y);
  1104.     fmprstr(ids->name);
  1105.     info_y -= 5;
  1106.  
  1107.     cpack(INFO_TEXT);
  1108.     /* File type */
  1109.     info_y -= info_line_space;
  1110.     cmov2i(info_left_margin, info_y);
  1111.     fmprstr((*ImageFn[ids->iType].Info)(ids, info_buf));
  1112.  
  1113.     /* Image dimensions */
  1114.     info_y -= info_line_space;
  1115.     cmov2i(info_left_margin, info_y);
  1116.     sprintf(info_buf, "%4d x %4d pixels, %d component%s",
  1117.     ids->xsize, ids->ysize, ids->zsize, ids->zsize>1 ? "s" : "");
  1118.     fmprstr(info_buf);
  1119.  
  1120.     /* File size */
  1121.     info_y -= info_line_space;
  1122.     cmov2i(info_left_margin, info_y);
  1123.     Commafy(num_buf, ids->size);
  1124.     sprintf(info_buf, "%s bytes", num_buf);
  1125.     fmprstr(info_buf);
  1126.  
  1127.     /* Modification time */
  1128.     info_y -= info_line_space;
  1129.     cmov2i(info_left_margin, info_y);
  1130.     cftime(info_buf, "%a %b %e %r %Y", (time_t *) &ids->mtime);
  1131.     fmprstr(info_buf);
  1132. }
  1133.  
  1134.  
  1135. void
  1136. Commafy(char *s, int n) {
  1137.     char buf[32], *src, *dest;
  1138.     int l;
  1139.  
  1140.     sprintf(buf, "%d", n);
  1141.     for (l = strlen(buf), src = buf, dest = s; l > 0; l--, src++, dest++) {
  1142.     *dest = *src;
  1143.     if (l > 1 && l % 3 == 1) *(++dest) = ',';
  1144.     }
  1145.     *dest = '\0';
  1146. }
  1147.  
  1148. void
  1149. DrawGenericInfo() {
  1150.     int info_y;
  1151.     char info_buf[PATH_MAX + 64];
  1152.  
  1153.     cpack(INFO_NAME);
  1154.     /* Number of images */
  1155.     info_y = info_area_height - info_line_space - 5;
  1156.     cmov2i(info_left_margin, info_y);
  1157.     sprintf(info_buf, "%d images found", nDisplayIcons);
  1158.     fmprstr(info_buf);
  1159.     info_y -= 5;
  1160.  
  1161.     cpack(INFO_TEXT);
  1162.     /* General help text */
  1163.     info_y -= info_line_space;
  1164.     cmov2i(info_left_margin, info_y);
  1165.     fmprstr(GetIdleModeName(idleMode));
  1166.  
  1167.     info_y -= info_line_space;
  1168.     cmov2i(info_left_margin, info_y);
  1169.     fmprstr("Move mouse over icon for more info.");
  1170.  
  1171.     info_y -= info_line_space;
  1172.     cmov2i(info_left_margin, info_y);
  1173.     fmprstr("Double-click left mouse on icon to open.");
  1174.  
  1175.     info_y -= info_line_space;
  1176.     cmov2i(info_left_margin, info_y);
  1177.     fmprstr("Drag scroll bar with left mouse to scan.");
  1178. }
  1179.  
  1180. void
  1181. DrawScrollBar() {
  1182.     /* Draw the scroll bar */
  1183.     cpack(SCROLL_BG);
  1184.     rectfi(0,info_area_height-1,scroll_bar_width-1,win_sy-1);
  1185.     cpack(SCROLL_FRAME);
  1186.     recti(0,info_area_height-1,scroll_bar_width-1,win_sy-1);
  1187.     cpack(SCROLL_THUMB);
  1188.     rectfi(1,scroll_thumb_bottom,scroll_bar_width-2,scroll_thumb_top-1);
  1189.     cpack(SCROLL_FRAME);
  1190.     recti(1,scroll_thumb_bottom,scroll_bar_width-2,scroll_thumb_top-1);
  1191. }
  1192.  
  1193. void
  1194. RedrawActive() {
  1195.     int icon_i, icon_j;
  1196.     int xorg, yorg, xoff, yoff, xsize, ysize;
  1197.  
  1198.     frontbuffer(TRUE);
  1199.  
  1200.     /* Unhighlight old active icon and highlight new in the front buffer */
  1201.     scrmask(scroll_bar_width, win_sx-1, info_area_height, win_sy-1);
  1202.     if (activeIcon >= 0) {
  1203.     WhereIsIcon(activeIcon,
  1204.         &icon_i, &icon_j, &xsize, &ysize, &xoff, &yoff, &xorg, &yorg);
  1205.     cpack(ICON_BG);
  1206.     recti(xorg+xoff-4, yorg+yoff-4,
  1207.         xorg+xoff+xsize+4, yorg+yoff+ysize+4);
  1208.     }
  1209.  
  1210.     fmsetfont(infoFont);
  1211.     activeIcon = newActiveIcon;
  1212.     if (activeIcon >= 0) {
  1213.     WhereIsIcon(activeIcon,
  1214.         &icon_i, &icon_j, &xsize, &ysize, &xoff, &yoff, &xorg, &yorg);
  1215.     cpack(ICON_ACTIVE);
  1216.     recti(xorg+xoff-4, yorg+yoff-4,
  1217.         xorg+xoff+xsize+4, yorg+yoff+ysize+4);
  1218.     }
  1219.  
  1220.     /* Clear and redraw info area */
  1221.     scrmask(0, win_sx-1, 0, info_area_height-1);
  1222.     cpack(INFO_BG);
  1223.     rectfi(0,0,win_sx-1,info_area_height-1);
  1224.     cpack(INFO_FRAME);
  1225.     recti(0,0,win_sx-1,info_area_height-1);
  1226.     if (activeIcon >= 0)
  1227.     DrawIconInfo();
  1228.     else
  1229.     DrawGenericInfo();
  1230.  
  1231.     frontbuffer(FALSE);
  1232.     scrmask(0, win_sx-1, 0, win_sy-1);
  1233. }
  1234.  
  1235.  
  1236.  
  1237. /*****************************************************************************
  1238.  *
  1239.  * Routines for second process - Read directory, check type, iconify, etc.
  1240.  * This process watches the flag "needToSwitch" for directory changes.
  1241.  * To get the required responsiveness, The work to be done is controlled by a
  1242.  * state machine of several sequential modes, as indicated by "idleMode".
  1243.  * Within each mode, the task is segmented so that the main processing
  1244.  * routine, "IdleProcess", does one thing and returns.  These tasks are
  1245.  * controlled by global variables such as idleFile, loFree, hiFull, etc.
  1246.  *
  1247.  *****************************************************************************/
  1248.  
  1249. void
  1250. IdleLoop() {
  1251.     readerPID = getpid();
  1252.     for (;;) {
  1253.     IdleProcess();
  1254.     if (needToFinish) {
  1255.         FinishDirectory();
  1256.         okayToFinish = TRUE;
  1257.         blockproc(readerPID);
  1258.     }
  1259.     if (needToSwitch) {
  1260.         needToSwitch = FALSE;
  1261.         if (idleMode != Before) FinishDirectory();
  1262.         StartDirectory(newDirName);
  1263.         StartReadDir();
  1264.         switchDone = TRUE;
  1265.     }
  1266.     }
  1267. }
  1268.  
  1269.  
  1270. /* Do idle processing - do one thing and return */
  1271. void
  1272. IdleProcess() {
  1273.     int iIcon, i, iEnt, cnt, loFree, hiFull;
  1274.     struct iconDirStruct *ids;
  1275.     
  1276.     switch (idleMode) {
  1277.     case ReadDir:    if (!ProcessReadDir())    StartCheckTimes(); break;
  1278.     case CheckTimes: if (!ProcessCheckTimes()) StartChecking();   break;
  1279.     case Checking:   if (!ProcessChecking())   StartReadIcons();  break;
  1280.     case ReadIcons:  if (!ProcessReadIcons())  StartIconifying(); break;
  1281.     case Iconifying: if (!ProcessIconifying()) StartCompacting(); break;
  1282.     case Compacting: if (!ProcessCompacting()) StartSaveFiles();  break;
  1283.     case SaveFiles:  if (!ProcessSaveFiles())  StartDone();       break;
  1284.  
  1285.     default:
  1286.     /* Nothing to do. Slow down this busy loop */
  1287.     sginap(5);
  1288.     break;
  1289.     }
  1290. }
  1291.  
  1292.  
  1293. /*
  1294.  * Try to open directory and icon files for read/write in this directory.
  1295.  * If directory or icon file cannot be opened to write, clear canWriteIconFiles
  1296.  * flag.  If directory and icon files exist, open them for read, even if they
  1297.  * never get updated.
  1298.  */
  1299.  
  1300. void
  1301. StartDirectory(char *name) {
  1302.     char buf[PATH_MAX];
  1303.     int i;
  1304.     struct stat iconStatBuf, iconDirStatBuf;
  1305.  
  1306.     /* Check for the presence of this directory */
  1307.     if (chdir(name) < 0)
  1308.     switch (errno) {
  1309.     case ENOENT:
  1310.         fprintf(stderr, "%s: No such directory: %s/%s\n",
  1311.         cmdName, theDirName, name);
  1312.         break;
  1313.     case EACCES:
  1314.         fprintf(stderr, "%s: Access denied: %s/%s\n",
  1315.         cmdName, theDirName, name);
  1316.         break;
  1317.     default:
  1318.         fprintf(stderr, "%s: Cannot open directory: %s/%s\n",
  1319.         cmdName, theDirName, name);
  1320.         break;
  1321.     }
  1322.     getwd(theDirName);
  1323.  
  1324.     /* Try to open icon directory file */
  1325.     nDirEnts = 0;
  1326.     canWriteIconFiles = !neverWriteIconFiles;
  1327.     sprintf(buf, "%s/.ibrowse_dir", theDirName);
  1328.     iconDirFD = -1;
  1329.     if (canWriteIconFiles) iconDirFD = open(buf, O_RDWR | O_CREAT, 0666);
  1330.     if (iconDirFD < 0) {
  1331.     canWriteIconFiles = FALSE;
  1332.     iconDirFD = open(buf, O_RDONLY);
  1333.     }
  1334.     needToWriteDirFile = FALSE;
  1335.  
  1336.     /* Try to open icon file */
  1337.     sprintf(buf, "%s/.ibrowse_icon", theDirName);
  1338.     iconFD = -1;
  1339.     if (canWriteIconFiles) iconFD = open(buf, O_RDWR | O_CREAT, 0666);
  1340.     if (iconFD < 0) {
  1341.     canWriteIconFiles = FALSE;
  1342.     iconFD = open(buf, O_RDONLY);
  1343.     }
  1344.     iconStatBuf.st_size = 0;
  1345.     if (iconFD >= 0) fstat(iconFD, &iconStatBuf);
  1346.     nIconFileEnts = iconStatBuf.st_size / ICONSPACE;
  1347.     for (i=0; i<nIconFileEnts; i++) {
  1348.     icons[i] = NULL;
  1349.     iconRef[i] = -1;
  1350.     }
  1351.  
  1352.     /* If either icon file seems to have the wrong record length, force a
  1353.        rewrite, or at least don't read it. */
  1354.     if (iconDirFD >= 0) {
  1355.     fstat(iconDirFD, &iconDirStatBuf);
  1356.     if (iconStatBuf.st_size % ICONSPACE != 0 ||
  1357.         iconDirStatBuf.st_size % sizeof(struct iconDirStruct) != 0)
  1358.         if (canWriteIconFiles) {
  1359.         /* Truncate the files to 0 length */
  1360.         ftruncate(iconDirFD, 0);
  1361.         ftruncate(iconFD, 0);
  1362.         } else {
  1363.         /* Close the directory file so it doesn't get read. */
  1364.         close(iconDirFD);
  1365.         iconDirFD = -1;
  1366.         }
  1367.     }
  1368. }
  1369.  
  1370. void
  1371. FinishDirectory() {
  1372.     char buf[PATH_MAX+32];
  1373.     int iEnt, iIcon;
  1374.  
  1375.     /* Tie up loose ends and exit */
  1376.     if (needToWriteDirFile && canWriteIconFiles) WriteDirFile();
  1377.     if (iconDirFD >= 0) close(iconDirFD);
  1378.     if (iconFD >= 0)    close(iconFD);
  1379.  
  1380.     /* Leave no files behind if there are no images in this directory.
  1381.      * Even though this makes us scan all the files next time
  1382.      * we visit, avoid cluttering up the file system with .ibrowse... files.
  1383.      */
  1384.     if (nIcons <= 0) {
  1385.     sprintf(buf, "/bin/rm %s/.ibrowse_dir 1>/dev/null 2>&1", theDirName);
  1386.     system(buf);
  1387.     sprintf(buf, "/bin/rm %s/.ibrowse_icon 1>/dev/null 2>&1", theDirName);
  1388.     system(buf);
  1389.     }
  1390.  
  1391.     /* Unallocate malloc'd memory */
  1392.     for (iEnt = 0; iEnt < nDirEnts; iEnt++) {
  1393.     free(myDirList[iEnt]);
  1394.     myDirList[iEnt] = NULL;
  1395.     }
  1396.     for (iIcon = 0; iIcon < nIconFileEnts; iIcon++)
  1397.     if (icons[iIcon] != NULL) {
  1398.         free(icons[iIcon]);
  1399.         icons[iIcon] = NULL;
  1400.     }
  1401.     nDirEnts = 0;
  1402.     nIconFileEnts = 0;
  1403.     nIcons = 0;
  1404.     needToCount = TRUE;
  1405.     needToDraw = TRUE;
  1406. }
  1407.  
  1408. void
  1409. WriteDirFile() {
  1410.     int iEnt;
  1411.  
  1412.     /* Rewrite dirty entries of entire directory file on demand. */
  1413.     if (canWriteIconFiles && iconDirFD >= 0)
  1414.     for (iEnt = 0; iEnt < nDirEnts; iEnt++)
  1415.         if ((myDirList[iEnt]->flags & DIRTYFLAG) != 0) WriteDirEnt(iEnt);
  1416.  
  1417.     needToWriteDirFile = FALSE;
  1418. }
  1419.  
  1420. /* Force write of a single directory entry */
  1421. void
  1422. WriteDirEnt(int iEnt) {
  1423.     int len;
  1424.     char buf[PATH_MAX];
  1425.  
  1426.     myDirList[iEnt]->flags &= ~DIRTYFLAG;
  1427.     lseek(iconDirFD, iEnt * sizeof(struct iconDirStruct), SEEK_SET);
  1428.     len = write(iconDirFD, myDirList[iEnt], sizeof(struct iconDirStruct));
  1429.     if (len < (int) sizeof(struct iconDirStruct)) {
  1430.     sprintf(buf, "%s: Write %s/.ibrowse_dir\n", cmdName, theDirName);
  1431.     perror(buf);
  1432.     exit(1);
  1433.     }
  1434. }
  1435.  
  1436.  
  1437. /*****************************************************************************
  1438.  *
  1439.  * Background process routines to browse icon files rather than scan directory.
  1440.  *
  1441.  *****************************************************************************/
  1442.  
  1443. void
  1444. FileIdleLoop() {
  1445.     readerPID = getpid();
  1446.     OpenIconFiles();
  1447.     StartReadDir();
  1448.     for (;;) {
  1449.     FileIdleProcess();
  1450.     if (needToFinish) {
  1451.         FinishDirectory();
  1452.         okayToFinish = TRUE;
  1453.         blockproc(readerPID);
  1454.     }
  1455.     }
  1456. }
  1457.  
  1458.  
  1459. /* Do idle processing - do one thing and return */
  1460. void
  1461. FileIdleProcess() {
  1462.     int iIcon, i, iEnt, cnt, loFree, hiFull;
  1463.     struct iconDirStruct *ids;
  1464.     
  1465.     switch (idleMode) {
  1466.     case ReadDir:    if (!ProcessReadDir())    StartRegister();   break;
  1467.     case Register:   if (!ProcessRegister())   StartReadIcons();  break;
  1468.     case ReadIcons:  if (!ProcessReadIcons())  StartDone();       break;
  1469.  
  1470.     default:
  1471.     /* Nothing to do. Slow down this busy loop */
  1472.     sginap(5);
  1473.     break;
  1474.     }
  1475. }
  1476.  
  1477. void
  1478. OpenIconFiles() {
  1479.     char buf[PATH_MAX];
  1480.  
  1481.     sprintf(buf, "%s.dir", iconRootName);
  1482.     if ((iconDirFD = open(buf, O_RDONLY)) < 0) {
  1483.     fprintf(stderr, "Cannot open directory file: %s\n", buf);
  1484.     exit(1);
  1485.     }
  1486.     sprintf(buf, "%s.icon", iconRootName);
  1487.     if ((iconFD = open(buf, O_RDONLY)) < 0) {
  1488.     fprintf(stderr, "Cannot open icon file: %s\n", buf);
  1489.     exit(1);
  1490.     }
  1491.     canWriteIconFiles = FALSE;
  1492. }
  1493.  
  1494. /*****************************************************************************
  1495.  *
  1496.  * Idle processing routines
  1497.  *
  1498.  *****************************************************************************/
  1499.  
  1500. /*
  1501.  * Read icon directory file entries into memory.
  1502.  */
  1503. void
  1504. StartReadDir() {
  1505.     /* Set up to start reading */
  1506.     if (!canWriteIconFiles && updateOnlyFlag)
  1507.     /* We're just here to update and we can't write - Give up. */
  1508.     StartDone();
  1509.     else {
  1510.     idleMode = ReadDir;
  1511.     needToDraw = TRUE;    /* Update status message */
  1512.     }
  1513. }
  1514.  
  1515. int
  1516. ProcessReadDir() {
  1517.     struct iconDirStruct dirBuf;
  1518.     /*
  1519.      * Allocate memory and read next entry.
  1520.      * Return TRUE iff read is successful.
  1521.      */
  1522.     if (iconDirFD < 0) return FALSE;
  1523.  
  1524.     if (nDirEnts > MAXDIRENTS) {
  1525.     fprintf(stderr,
  1526.         "%s: Error: Too many directory entries (> %d)\n",
  1527.         cmdName, MAXDIRENTS);
  1528.     exit(1);
  1529.     }
  1530.     if (read(iconDirFD, &dirBuf, sizeof(dirBuf)) < (int) sizeof(dirBuf))
  1531.     return FALSE;
  1532.     dirBuf.index = nDirEnts;
  1533.     dirBuf.flags = UNREFFLAG; /* Clear dirty and ref bits */
  1534.     myDirList[nDirEnts] =
  1535.     (struct iconDirStruct *) malloc(sizeof(struct iconDirStruct));
  1536.     *myDirList[nDirEnts] = dirBuf;
  1537.     nDirEnts++;
  1538.     return TRUE;
  1539. }
  1540.  
  1541.  
  1542. /*
  1543.  * Verify that directory entries make sense and add them to the display list.
  1544.  */
  1545. void
  1546. StartRegister() {
  1547.     /* Set up to start adding icons */
  1548.     idleFile = 0;
  1549.     idleMode = Register;
  1550.     needToDraw = TRUE;    /* Update status message */
  1551. }
  1552.  
  1553. int
  1554. ProcessRegister() {
  1555.     struct iconDirStruct *ids;
  1556.     int iType;
  1557.  
  1558.     for (; idleFile < nDirEnts; idleFile++) {
  1559.     ids = myDirList[idleFile];
  1560.     /* Add image to icon display list */
  1561.     if (ids->state == NEED2ICONIFY ||
  1562.         ids->state == ISIMAGE) {
  1563.         for (iType = 0;; iType++) {
  1564.         if (iType >= nImageTypes) {
  1565.             /* Unrecognized image type ID */
  1566.             ids->state = NEED2CHECK;
  1567.             break;
  1568.         }
  1569.         if (ImageFn[iType].type == ids->type) {
  1570.             /* Found the image type ID. Remember the index. */
  1571.             ids->iType = iType;
  1572.             AddIcon(idleFile);
  1573.             idleFile++;
  1574.             return TRUE;
  1575.         }
  1576.         }
  1577.     }
  1578.     }
  1579.     return FALSE;
  1580. }
  1581.  
  1582.  
  1583. /*
  1584.  * Check each directory entry's modification time against the corresponding
  1585.  * entry in the icon directory file to determine if the file type and
  1586.  * iconic version are valid.
  1587.  */
  1588. void
  1589. StartCheckTimes() {
  1590.     /* Set up to start checking. */
  1591.     idleMode = CheckTimes;
  1592.     nBusyDirList = 0;
  1593.     if ((theDir = opendir(theDirName)) == NULL) {
  1594.     fprintf(stderr, "%s: Cannot open directory %s\n",
  1595.         cmdName, theDirName);
  1596.     StartDone();
  1597.     } else
  1598.     needToDraw = TRUE; /* Update status window */
  1599. }
  1600.  
  1601. int
  1602. ProcessCheckTimes() {
  1603.     int iEnt;
  1604.     struct dirent *dirEnt;
  1605.  
  1606.     /* Check next directory entry. Return TRUE iff more files to check. */
  1607.     if ((dirEnt = readdir(theDir)) != NULL) {
  1608.     /* Check the next directory entry (ignore '.' and '..') */
  1609.     if (strcmp(dirEnt->d_name, ".") != 0 &&
  1610.         strcmp(dirEnt->d_name, "..") != 0) CheckFile(dirEnt->d_name);
  1611.     } else {
  1612.     /* No more directory entries to check.  Finish up. */
  1613.     closedir(theDir);
  1614.  
  1615.     /* Flag unreferenced directory file entries */
  1616.     for (iEnt = 0; iEnt < nDirEnts; iEnt++)
  1617.         if ((myDirList[iEnt]->flags & REFFLAG) == 0) {
  1618.         myDirList[iEnt]->flags |= DIRTYFLAG;
  1619.         myDirList[iEnt]->state = NOFILE;
  1620.         }
  1621.  
  1622.     if (needToWriteDirFile && canWriteIconFiles) WriteDirFile();
  1623.     SortDisplayList(CmpDirName);
  1624.  
  1625.     qsort(directoryList, nBusyDirList, sizeof(int),
  1626.         (int (*)(const void *, const void *)) CmpDirName);
  1627.     needToUpdateDirectory = TRUE; /* Put sorted directories on menu */
  1628.     return FALSE;
  1629.     }
  1630.     return TRUE;
  1631. }
  1632.  
  1633.  
  1634. /*
  1635.  * Check whether unknown files are images or not.
  1636.  */
  1637. void
  1638. StartChecking() {
  1639.     /* Set up to check file type. */
  1640.     idleFile = 0;
  1641.     idleMode = Checking;
  1642.     needToDraw = TRUE; /* Update status window */
  1643. }
  1644.  
  1645. int
  1646. ProcessChecking() {
  1647.     /* Check next file. Return TRUE iff there are more files to check. */
  1648.     for (;; idleFile++) {
  1649.     if (idleFile >= nDirEnts) {
  1650.         /* Done checking. Finish up. */
  1651.         if (needToWriteDirFile && canWriteIconFiles) WriteDirFile();
  1652.         return FALSE;
  1653.     }
  1654.     if (myDirList[idleFile]->state == NEED2CHECK) {
  1655.         if (CheckImage(myDirList[idleFile])) {
  1656.         AddIcon(idleFile);
  1657.         needToDrawEventually = TRUE;
  1658.         }
  1659.         idleFile++;
  1660.         return TRUE;
  1661.     }
  1662.     }
  1663. }
  1664.  
  1665.  
  1666. /*
  1667.  * Read already iconified images from icon file.
  1668.  */
  1669. void
  1670. StartReadIcons() {
  1671.     /* Set up to read icons. */
  1672.     idleFile = 0;
  1673.     idleMode = ReadIcons;
  1674.     needToDraw = TRUE; /* Update status window */
  1675. }
  1676.  
  1677. int
  1678. ProcessReadIcons() {
  1679.     int iIcon;
  1680.     struct iconDirStruct *ids;
  1681.  
  1682.     /* Read the next icon. Return TRUE iff more icons to read. */
  1683.     ids = NULL;
  1684.  
  1685.     /* Check visible icons first for ones to be read */
  1686.     if (needToReadQuick)
  1687.     for (iIcon = iFirstVisible;; iIcon++) {
  1688.         if (iIcon > iLastVisible) {
  1689.         /* Found no more icons to quick read. */
  1690.         ids = NULL;
  1691.         needToReadQuick = FALSE;
  1692.         break;
  1693.         }
  1694.         ids = myDirList[displayList[iIcon]];
  1695.         if (ids->state == ISIMAGE && icons[ids->iicon] == NULL) {
  1696.         /* This one's visible.  Update it eventually. */
  1697.         needToDrawEventually = TRUE;
  1698.         break;
  1699.         }
  1700.     }
  1701.  
  1702.     /* If no visible icons to be read, look for next one in list */
  1703.     if (ids == NULL)
  1704.     for (;;) {
  1705.         if (idleFile >= nDirEnts)
  1706.         /* Done reading icons from disk */
  1707.         return FALSE;
  1708.  
  1709.         ids = myDirList[idleFile++];
  1710.         if (ids->state == ISIMAGE && icons[ids->iicon] == NULL) break;
  1711.     }
  1712.  
  1713.     /* If we've found one to read, do it */
  1714.     if (ids != NULL) ReadIcon(ids);
  1715.     return TRUE;
  1716. }
  1717.  
  1718.  
  1719. /*
  1720.  * Iconify image files and save the icon to disk.
  1721.  */
  1722. void
  1723. StartIconifying() {
  1724.     /* Set up to start iconifying image files */
  1725.     idleFile = 0;
  1726.     idleMode = Iconifying;
  1727.     needToDraw = TRUE; /* Update status window */
  1728. }
  1729.  
  1730. int
  1731. ProcessIconifying() {
  1732.     int iIcon;
  1733.     struct iconDirStruct *ids;
  1734.  
  1735.     /* Iconify the next image.  Return TRUE iff more files to iconify */
  1736.     ids = NULL;
  1737.  
  1738.     /* Iconify visible icons first */
  1739.     if (needToIconifyQuick)
  1740.     for (iIcon = iFirstVisible;; iIcon++) {
  1741.         if (iIcon > iLastVisible) {
  1742.         ids = NULL;
  1743.         needToIconifyQuick = FALSE;
  1744.         break;
  1745.         }
  1746.         ids = myDirList[displayList[iIcon]];
  1747.         if (ids->state == NEED2ICONIFY) {
  1748.         needToDrawEventually = TRUE;
  1749.         break;
  1750.         }
  1751.     }
  1752.  
  1753.     /* If no visible icons to be iconified, look for next one in list */
  1754.     if (ids == NULL)
  1755.     for (;;) {
  1756.         if (idleFile >= nDirEnts)
  1757.         /* End of stuff to iconify. Go to next state. */
  1758.         return FALSE;
  1759.  
  1760.         ids = myDirList[idleFile++];
  1761.         if (ids->state == NEED2ICONIFY) break;
  1762.     }
  1763.  
  1764.     /* If we've found one to iconify, do it */
  1765.     if (ids != NULL) {
  1766.     ImageToIcon(ids);
  1767.     if (canWriteIconFiles) WriteIcon(ids);
  1768.     }
  1769.     return TRUE;
  1770. }
  1771.  
  1772.  
  1773. /*
  1774.  * Rearrange icon file entries and truncate to eliminate wasted space on disk.
  1775.  */
  1776. void
  1777. StartCompacting() {
  1778.     /* Set up to compact icon files */
  1779.     if (canWriteIconFiles) {
  1780.     idleMode = Compacting;
  1781.     needToDraw = TRUE;
  1782.     } else
  1783.     /* Can't write anything, so give up at this point. */
  1784.     StartDone();
  1785. }
  1786.  
  1787. int
  1788. ProcessCompacting() {
  1789.     int i, loFree, hiFull;
  1790.     struct iconDirStruct *ids;
  1791.  
  1792.     /*
  1793.      * Compact the next icon file -
  1794.      * All icons are in memory at this point. Icon files are writable.
  1795.      * Return TRUE iff more of the files to check.
  1796.      */
  1797.     loFree = -1;
  1798.     hiFull = nIconFileEnts;
  1799.     for (;;) {
  1800.     /* Find next free/full entries */
  1801.     while (loFree < 0 || iconRef[loFree] >= 0)
  1802.         if (++loFree >= nIconFileEnts) break;
  1803.     while (hiFull >= nIconFileEnts || iconRef[hiFull] < 0)
  1804.         if (--hiFull < 0) break;
  1805.     
  1806.     /* Check for completion - no free slots or all at end of file */
  1807.     if (loFree > hiFull) break;
  1808.  
  1809.     /* Move the high icon into the low free slot */
  1810.     lseek(iconFD, loFree * ICONSPACE, SEEK_SET);
  1811.     write(iconFD, icons[hiFull], ICONSPACE);
  1812.     icons[loFree] = icons[hiFull];
  1813.     icons[hiFull] = NULL;
  1814.     iconRef[loFree] = iconRef[hiFull];
  1815.     iconRef[hiFull] = -1;
  1816.     myDirList[iconRef[loFree]]->iicon = loFree;
  1817.     WriteDirEnt(iconRef[loFree]);
  1818.     }
  1819.  
  1820.     /* Truncate icon file */
  1821.     if (loFree < nIconFileEnts) {
  1822.     lseek(iconFD, 0, SEEK_SET);
  1823.     nIconFileEnts = loFree;
  1824.     ftruncate(iconFD, nIconFileEnts * ICONSPACE);
  1825.     }
  1826.  
  1827.     /* Compact icon directory file */
  1828.     loFree = -1;
  1829.     hiFull = nDirEnts;
  1830.     for (;;) {
  1831.     /* Find next free/full entries */
  1832.     while (loFree < 0 || (myDirList[loFree]->flags & REFFLAG))
  1833.         if (++loFree >= nDirEnts) break;
  1834.     while (hiFull >= nDirEnts ||
  1835.         ((myDirList[hiFull]->flags & REFFLAG) == 0))
  1836.         if (--hiFull < 0) break;
  1837.     
  1838.     /* Check for completion - no free slots or all at end of dir */
  1839.     if (loFree > hiFull) break;
  1840.  
  1841.     /* Move the high directory entry into the low free slot */
  1842.     /* Set up new entry */
  1843.     ids = myDirList[loFree];
  1844.     myDirList[loFree] = myDirList[hiFull];
  1845.     myDirList[loFree]->index = loFree;
  1846.     WriteDirEnt(loFree);
  1847.     iconRef[myDirList[loFree]->iicon] = loFree;
  1848.     for (i = 0; i < nIcons; i++)
  1849.         if (displayList[i] == hiFull) {
  1850.         displayList[i] = loFree;
  1851.         break;
  1852.         }
  1853.  
  1854.     /* Clear out old entry */
  1855.     myDirList[hiFull] = ids;
  1856.     myDirList[hiFull]->index = hiFull;
  1857.     WriteDirEnt(hiFull);
  1858.     }
  1859.  
  1860.     /* Free unused directory entries and truncate icon directory file */
  1861.     if (loFree < nDirEnts) {
  1862.     for (i= loFree; i< nDirEnts; i++) {
  1863.         free(myDirList[i]);
  1864.         myDirList[i] = NULL;
  1865.     }
  1866.  
  1867.     lseek(iconDirFD, 0, SEEK_SET);
  1868.     nDirEnts = loFree;
  1869.     ftruncate(iconDirFD, nDirEnts * sizeof(struct iconDirStruct));
  1870.     }
  1871.  
  1872.     return FALSE;
  1873. }
  1874.  
  1875.  
  1876. /*
  1877.  * Save any unsaved changes to icon files.
  1878.  */
  1879. void
  1880. StartSaveFiles() {
  1881.     /* Set up to save icon files to disk */
  1882.     idleMode = SaveFiles;
  1883.     needToDraw = TRUE; /* Update status window */
  1884. }
  1885.  
  1886. int
  1887. ProcessSaveFiles() {
  1888.     /* Save any unsaved changed to disk. For now, do it all at once. */
  1889.     if (needToWriteDirFile && canWriteIconFiles) WriteDirFile();
  1890.     return FALSE;
  1891. }
  1892.  
  1893.  
  1894. /*
  1895.  * All done, just sit there.
  1896.  */
  1897. void
  1898. StartDone() {
  1899.     /* No more to do - for now */
  1900.     idleMode = Done;
  1901.     needToDraw = TRUE; /* Update status window */
  1902. }
  1903.  
  1904.  
  1905. /*****************************************************************************
  1906.  *
  1907.  * Support routines for idle processing
  1908.  *
  1909.  *****************************************************************************/
  1910.  
  1911. void
  1912. CheckFile(char *filename) {
  1913.     int iEnt, iType;
  1914.     struct stat statBuf;
  1915.     char name_buf[512];
  1916.     struct iconDirStruct *ids;
  1917.  
  1918.     /* Try to find this file in the existing list */
  1919.     for (iEnt = 0; iEnt < nDirEnts; iEnt++) {
  1920.     if (strcmp(filename, (ids = myDirList[iEnt])->name) == 0) break;
  1921.     }
  1922.     /* Create an entry if one does not exist */
  1923.     if (iEnt >= nDirEnts) {
  1924.     iEnt = nDirEnts;
  1925.     ids = myDirList[iEnt] =
  1926.         (struct iconDirStruct *) malloc(sizeof(struct iconDirStruct));
  1927.     nDirEnts++;
  1928.     if (nDirEnts > MAXDIRENTS) {
  1929.         fprintf(stderr,
  1930.         "%s: Error: Too many directory entries (> %d)\n",
  1931.         cmdName, MAXDIRENTS);
  1932.         exit(1);
  1933.     }
  1934.     strcpy(ids->name, filename);
  1935.     ids->index  = iEnt;
  1936.     ids->state  = NEED2CHECK;
  1937.     ids->iicon  = -1;
  1938.     ids->mtime  = -1;
  1939.     ids->size   = -1;
  1940.     ids->flags |= DIRTYFLAG;
  1941.     needToWriteDirFile = TRUE;
  1942.     }
  1943.     ids->flags |= REFFLAG;
  1944.  
  1945.     /* Get file status */
  1946.     sprintf(name_buf, "%s/%s", theDirName, filename);
  1947.     if (stat(name_buf, &statBuf) < 0)
  1948.     ids->state = CANTREAD;
  1949.  
  1950.     /* Add directories to the browser list, mark as not images */
  1951.     if (S_ISDIR(statBuf.st_mode)) {
  1952.     directoryList[nBusyDirList++] = iEnt;
  1953.     ids->state = NOTIMAGE;
  1954.     return;
  1955.     }
  1956.  
  1957.     /* Mark anything else not a regular file or sym link as not an image. */
  1958.     if (!S_ISREG(statBuf.st_mode) && !S_ISLNK(statBuf.st_mode)) {
  1959.     ids->state = NOTIMAGE;
  1960.     return;
  1961.     }
  1962.  
  1963.     /* Check stored mtime against actual file */
  1964.     if (statBuf.st_mtime != ids->mtime) {
  1965.     ids->mtime = statBuf.st_mtime;
  1966.     ids->size  = statBuf.st_size;
  1967.     ids->state = NEED2CHECK;
  1968.     }
  1969.  
  1970.     /* If the icon index points beyond end of icon file, check again */
  1971.     if (ids->iicon >= nIconFileEnts) {
  1972.     ids->iicon = -1;
  1973.     ids->state = NEED2CHECK;
  1974.     }
  1975.  
  1976.     /* If this file has never been checked, try now */
  1977.     if (ids->state == CANTREAD)
  1978.     ids->state = NEED2CHECK;
  1979.  
  1980.     /* Add images to icon display list */
  1981.     if (ids->state == NEED2ICONIFY ||
  1982.     ids->state == ISIMAGE) {
  1983.     for (iType = 0;; iType++) {
  1984.         if (iType >= nImageTypes) {
  1985.         /* Unrecognized image type ID */
  1986.         ids->state = NEED2CHECK;
  1987.         break;
  1988.         }
  1989.         if (ImageFn[iType].type == ids->type) {
  1990.         /* Found the image type ID. Remember the index to fn lists. */
  1991.         ids->iType = iType;
  1992.         AddIcon(iEnt);
  1993.         break;
  1994.         }
  1995.     }
  1996.     }
  1997. }
  1998.  
  1999. int
  2000. CheckImage(struct iconDirStruct *ids) {
  2001.     int iType, fd;
  2002.     char buf[PATH_MAX];
  2003.  
  2004.     sprintf(buf, "%s/%s", theDirName, ids->name);
  2005.     if ((fd = open(buf, O_RDONLY)) < 0)
  2006.     ids->state = CANTREAD;
  2007.  
  2008.     else {
  2009.     for (iType = 0;; iType++) {
  2010.         if (iType >= nImageTypes) {
  2011.         /* Nothing recognizes the file; not an image */
  2012.         ids->state = NOTIMAGE;
  2013.         break;
  2014.         }
  2015.         lseek(fd, 0, SEEK_SET);
  2016.         if ((*ImageFn[iType].Check)(ids, fd)) {
  2017.         /* Found it; x|y|zsize are set, calculate an icon size */
  2018.         if (ids->xsize == 0 && ids->ysize == 0)
  2019.             ids->ixsize = ids->iysize = 1;
  2020.         else {
  2021.             ids->ixsize = ids->iysize = ICONSIZE;
  2022.             if (ids->xsize > ids->ysize) {
  2023.             ids->iysize *= ((float) ids->ysize) / ids->xsize;
  2024.             if (ids->iysize <= 0) ids->iysize = 1;
  2025.             } else {
  2026.             ids->ixsize *= ((float) ids->xsize) / ids->ysize;
  2027.             if (ids->ixsize <= 0) ids->ixsize = 1;
  2028.             }
  2029.         }
  2030.  
  2031.         ids->iType = iType;
  2032.         ids->type = ImageFn[iType].type;
  2033.         ids->state = NEED2ICONIFY;
  2034.         ids->flags |= DIRTYFLAG;
  2035.         needToWriteDirFile = TRUE;
  2036.         break;
  2037.         }
  2038.     }
  2039.  
  2040.     close(fd);
  2041.     }
  2042.  
  2043.     return ids->state == ISIMAGE || ids->state == NEED2ICONIFY;
  2044. }
  2045.  
  2046.  
  2047. /*
  2048.  * Read image file and subsample into memory slot
  2049.  */
  2050. void
  2051. ImageToIcon(struct iconDirStruct *ids) {
  2052.     int iPixel;
  2053.  
  2054.     /* Verify that space is allocated in memory for the icon */
  2055.     AllocateIcon(ids);
  2056.  
  2057.     if (!(*ImageFn[ids->iType].Iconify)(ids, icons[ids->iicon]))
  2058.     /* If image can't be read, black it out */
  2059.     for (iPixel = 0; iPixel < ids->ixsize * ids->iysize; iPixel++)
  2060.         icons[ids->iicon][iPixel] = 0x00000000;
  2061.  
  2062.     /* Mark this icon valid */
  2063.     ((char *)(icons[ids->iicon]))[ICONSPACE] = 1;
  2064.  
  2065.     ids->state = ISIMAGE;
  2066.     ids->flags |= DIRTYFLAG;
  2067.     needToWriteDirFile = TRUE;
  2068. }
  2069.  
  2070.  
  2071. /*
  2072.  * Icon list management routines
  2073.  */
  2074. void
  2075. AddIcon(int iEnt) {
  2076.     /* Add an icon to the display list. */
  2077.     if (nIcons >= MAXICONS) {
  2078.     fprintf(stderr,
  2079.         "%s: Error: Too many icons (> %d)\n",
  2080.         cmdName, MAXICONS);
  2081.     exit(1);
  2082.     }
  2083.  
  2084.     displayList[nIcons++] = iEnt;
  2085.     needToCount = TRUE;
  2086.     needToDrawEventually = TRUE;
  2087. }
  2088.  
  2089. void
  2090. AssignIcon(struct iconDirStruct *ids) {
  2091.     /* Assign an icon file/list slot for this icon. */
  2092.     if (ids->iicon < 0) {
  2093.     /* New icon. Need to assign an icon slot number */
  2094.     ids->iicon = nIconFileEnts++;
  2095.     icons[ids->iicon] = NULL;
  2096.     }
  2097.     iconRef[ids->iicon] = ids->index;
  2098. }
  2099.  
  2100. void
  2101. AllocateIcon(struct iconDirStruct *ids) {
  2102.     /* Allocate memory for this icon in icons[].
  2103.      * v 1.2.2: Allocate an extra byte at the end of the icon buffer
  2104.      * and set it to 1 only when the icon is valid.  Keeps the icon
  2105.      * from being displayed before it is completely read or iconified.
  2106.      */
  2107.     AssignIcon(ids);
  2108.     if (icons[ids->iicon] != NULL) return;
  2109.     icons[ids->iicon] = (unsigned long *) malloc(ICONSPACE + 1);
  2110.     ((char *)(icons[ids->iicon]))[ICONSPACE] = 0;
  2111. }
  2112.  
  2113. void
  2114. ReadIcon(struct iconDirStruct *ids) {
  2115.     AllocateIcon(ids);
  2116.     lseek(iconFD, ids->iicon*ICONSPACE, SEEK_SET);
  2117.     read(iconFD, icons[ids->iicon], ICONSPACE);
  2118.  
  2119.     /* Mark this icon valid */
  2120.     ((char *)(icons[ids->iicon]))[ICONSPACE] = 1;
  2121. }
  2122.  
  2123. void
  2124. WriteIcon(struct iconDirStruct *ids) {
  2125.     lseek(iconFD, ids->iicon*ICONSPACE, SEEK_SET);
  2126.     write(iconFD, icons[ids->iicon], ICONSPACE);
  2127. }
  2128.  
  2129.  
  2130. /*
  2131.  * Miscellaneous routines
  2132.  */
  2133. char *
  2134. GetStateName(int state) {
  2135.     switch (state) {
  2136.     case NOFILE:       return "NOFILE";
  2137.     case NEED2CHECK:   return "NEED2CHECK";
  2138.     case CANTREAD:     return "CANTREAD";
  2139.     case ISIMAGE:      return "ISIMAGE";
  2140.     case NOTIMAGE:     return "NOTIMAGE";
  2141.     case NEED2ICONIFY: return "NEED2ICONIFY";
  2142.     default:           return "Unknown";
  2143.     }
  2144. }
  2145.  
  2146.  
  2147. char *
  2148. GetIdleModeName(int mode) {
  2149.     switch (mode) {
  2150.     case ReadDir:    return "Reading Icon files..." ;
  2151.     case Register:   return "Registering Images..." ;
  2152.     case CheckTimes: return "Scanning directory..." ;
  2153.     case Checking:   return "Looking for images..." ;
  2154.     case ReadIcons:  return "Reading icons from disk..." ;
  2155.     case Iconifying: return "Iconifying images..." ;
  2156.     case Compacting: return "Compacting files..." ;
  2157.     case SaveFiles:  return "Saving icon files..." ;
  2158.     default:         return "";
  2159.     }
  2160. }
  2161.  
  2162.  
  2163.  
  2164.